package token_jwt

import (
	"github.com/dgrijalva/jwt-go"
	"github.com/micro/cli"
	"github.com/micro/go-micro/v2/logger"
	"github.com/micro/micro/plugin"
	"net/http"
	"reflect"
	"time"
)

type Token struct {
	Name       string
	privateKey []byte
	UnAuthPath []string
}

type JwtPayloadInfo struct {
	UserName string
	jwt.StandardClaims
}

//定义一些参数，可以通过启动micro 的时候传参
func (l *Token) Flags() []cli.Flag {
	return []cli.Flag{cli.StringFlag{
		Name:   "token-jwt",
		Usage:  "需要从命令行传过来的参数",
		EnvVar: "HTTPLOG",
	}}
}
func (l *Token) Commands() []cli.Command {
	return nil
}

//处理程序 会在每次请求的时候调用
func (l *Token) Handler() plugin.Handler {
	return func(handler http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			//处理token 的逻辑

			logger.Infof(r.URL.Path)

			if  Contains(l.UnAuthPath,r.URL.Path)!=-1  {
				handler.ServeHTTP(w, r)
				return
			}
			authStr := r.Header.Get("auth")
			logger.Infof("authStr:%s",authStr)
			payload, err := l.Decode(authStr)
			if err != nil {
				logger.Info("auth fail")
				w.WriteHeader(http.StatusUnauthorized)
				return
			}
			logger.Infof("payload info= %v", payload)
			handler.ServeHTTP(w, r)
			return
		})
	}
}

//初始化数据,程序启动的时候会调用这个方法，可以在这里初始化一些参数
func (l *Token) Init(ctx *cli.Context) error {
	//从文件加载 公钥
	//加载公钥，放行链接等,存放到实例里面


	//初始化放行地址

	l.UnAuthPath=make([]string,0)
	l.UnAuthPath=append(l.UnAuthPath,"/login/token","/auth" )

	return nil
}
func (l *Token) String() string {

	return l.Name
}

//调用这个方法 new 插件，也可以在启动的时候这样子写
func NewPlugin() plugin.Plugin {
	return &Token{
		Name: "token-jwt",
	}
}

//Decode 解码
func (l *Token) Decode(tokenStr string) (*JwtPayloadInfo, error) {
	t, err := jwt.ParseWithClaims(tokenStr, &JwtPayloadInfo{}, func(token *jwt.Token) (interface{}, error) {
		return l.privateKey, nil
	})

	if err != nil {
		logger.Errorf("decode token error:%s",err.Error())
		return nil, err
	}
	// 解密转换类型并返回
	if claims, ok := t.Claims.(*JwtPayloadInfo); ok && t.Valid {
		logger.Infof("transfer payload ok:%v",ok)
		return claims, nil
	}

	return nil, err
}

// Encode 将 User 用户信息加密为 JWT 字符串
// expireTime := time.Now().Add(time.Hour * 24 * 3).Unix() 三天后过期
func (l *Token) Encode(issuer, userName string, expireTime int64) (string, error) {
	claims := JwtPayloadInfo{
		userName,
		jwt.StandardClaims{
			Issuer:    issuer,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: expireTime,
		},
	}
	logger.Infof("encode token info :%v",claims)
	jwtToken := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return jwtToken.SignedString(l.privateKey)
}

//判断数组包含
func Contains(array interface{}, val interface{}) (index int) {
	index = -1
	switch reflect.TypeOf(array).Kind() {
	case reflect.Slice:
		{
			s := reflect.ValueOf(array)
			for i := 0; i < s.Len(); i++ {
				if reflect.DeepEqual(val, s.Index(i).Interface()) {
					index = i
					return
				}
			}
		}
	}
	return
}
